import copy
import json
import os
import time
import uuid
import yaml
import base64
from tools.http.req import send_json_request
from tools.lock.file_lock import FileLock
from tools.hash import hash
from tools.log import logger
from PIL import Image
failed=False
ttk=2
def update_ttk():
    global ttk
    global failed

    if failed:
        ttk=min(ttk+60,1200)
    else:
        ttk=2    
def readafile(pending):
    try:
        with open(pending, "r", encoding="utf-8") as f:
            line = f.readline()
            return line.strip("\n") if line else None  # 空文件返回None
    except FileNotFoundError:
        logger.warning("找不到文件"+pending)
        return None  # 文件不存在返回None
    except IOError:
        logger.error("文件读取失败"+pending)
        return None  # 读取错误返回None
    
def removeafile(pending):
    with open(pending, 'r+') as f:
        lines = f.readlines()
        f.seek(0)
        f.truncate()
        f.writelines(lines[1:])

def load_yaml(yam):
    try:
        with open(yam+"meta.yaml","r",encoding='utf-8') as f:
            return yaml.safe_load(f)
    except Exception as e:
        logger.warning(f"找不到{yam}的元数据")
        return None

def save_yaml(yam,data):
    with open(yam+"meta.yaml","w",encoding='utf-8') as f:
        yaml.safe_dump(data,f)


def appendafile(recycle,file):
    with open(recycle,"a",encoding="utf-8") as w:
        w.write(file+"\n")

def bmp_to_png(input_path, output_path=None, compress_level=6):
    """
    将 BMP 转换为 PNG 并压缩
    :param input_path: BMP 文件路径
    :param output_path: 输出 PNG 文件路径 (可选，不传则同名替换后缀)
    :param compress_level: PNG 压缩等级 (0-9, 数字越大压缩率越高)
    """
    if output_path is None:
        output_path = os.path.splitext(input_path)[0] + ".png"

    # 如果目标文件已存在，直接返回
    if os.path.exists(output_path):
        print(f"跳过，文件已存在: {output_path}")
        return output_path

    with Image.open(input_path) as img:
        img.save(output_path, format="PNG", compress_level=compress_level)
    return output_path

def init_meta(file):
    meta = load_yaml(file)
    if not meta:
        return
    if meta["ready"]:
        return
    else:
        meta["image_ok"] = False
        meta["video_ok"] = False
        meta["upload"]={
            "image":{
                "id": meta["record"],
                "alarmTime":meta["alarm"],
                "carType":meta["vehicle"],	
                "imageData":None,
                "boxId":meta["box"]["id"],
                "cameraId":meta["camera_id"],
                "userName":meta["box"]["user"],
                "position":meta["position"],
                "direction":meta["direction"]     
                    },
            "video":{
                "task_id":None,
                "task_req":{
                    "alarmId":meta["record"],
                    "boxId":meta["box"]["id"],
                    "cameraId":meta["camera_id"],
                    "userName":meta["box"]["user"],
                    "requestNumber":str(uuid.uuid4())
                },
                "sha256": None,
                "chunk_dir": "chunks/",
                "chunk_nums":0,
                "chunks":[
                        
                ]
            }
    
        }
        meta["ready"]=True
        meta["splited"]=False

    save_yaml(file,meta)

def split_video(file,size):
    meta = load_yaml(file)
    if not meta or meta["only_image"]:
        return
    if meta["splited"]:
        logger.info(meta["id"]+"视频已经分割")
        return
    else:
        meta["upload"]["video"]["sha256"]=hash.sha256(meta["dir"]+meta["video"])
        meta["upload"]["video"]["chunks"]=split_file(
            input_file=meta["dir"]+meta["video"],
            output_dir=meta["dir"]+meta["upload"]["video"]["chunk_dir"],
            chunk_size=size
        )
        meta["upload"]["video"]["chunk_nums"]=len(meta["upload"]["video"]["chunks"])
        meta["splited"]=True
        logger.info(meta["id"]+"视频分割完成")
    save_yaml(file,meta)    

def image_to_base64_str(image_path: str) -> str:
    """将图片文件转换为Base64编码的字符串（如 'data:image/png;base64,...'）"""
    with open(image_path, "rb") as image_file:
        encoded_bytes = base64.b64encode(image_file.read())
        encoded_str = encoded_bytes.decode("utf-8")
    
    # # 根据文件类型添加MIME前缀（可选）
    # if image_path.lower().endswith(".png"):
    #     return f"data:image/png;base64,{encoded_str}"
    # elif image_path.lower().endswith((".jpg", ".jpeg")):
    #     return f"data:image/jpeg;base64,{encoded_str}"
    # else:
        return encoded_str  # 纯Base64，无MIME前缀
    
def upload_image(file,retry,url):
    global failed
    meta = load_yaml(file)
    if not meta:
        return
    if meta["image_ok"]:
        logger.info("图像已经上传")
        return
    else:
        if meta["image"].endswith(".bmp"):
            png = meta["image"].replace(".bmp",".png")
            bmp_to_png(meta["dir"]+meta["image"],meta["dir"]+png)
            meta["image"]=png
        upload_json=copy.deepcopy(meta["upload"]["image"])
        if not upload_json["imageData"]:
            upload_json["imageData"] = image_to_base64_str(meta["dir"]+meta["image"])
        # print(upload_json)
        res = send_json_request(
            url=url,
            retries=retry,
            payload=upload_json
        )
        if res["success"]:
            meta["image_ok"] = True  
            failed=False
            logger.info("图像上传成功")
        else:
            failed=True
            logger.warning("图像上传失败")
            print(res)
    save_yaml(file,meta)    

    
def upload_video(file,retry,url_req,url_video):
    meta = load_yaml(file)
    if not meta :
        return  
    if meta["only_image"]:
        meta["video_ok"]=True
        save_yaml(file,meta)  
    if meta["video_ok"]:
        return meta
    meta["video_ok"]=True
    upload_meta = meta["upload"]["video"]
    if not upload_meta["task_id"]:
        task_req_format=upload_meta["task_req"]
        res = send_json_request(
            url= url_req,
            payload=task_req_format
            ,retries=retry)
        if res["success"]:
            upload_meta["task_id"]=res["data"]["data"]["taskId"]
            logger.warning(f"任务创建{upload_meta['task_id']}")
        else:
            logger.warning(f"任务创建失败")
            return meta
    for idx in range(upload_meta["chunk_nums"]):
        chunk = upload_meta["chunks"][idx]
        if chunk["pended"]:
            continue
        else:
            chunk_format = {
            "boxId": meta["box"]["id"],
            "cameraId": meta["camera_id"],
            "userName":meta["box"]["user"],
            "taskId": upload_meta["task_id"],
            "videoId": meta["id"],
            "totalNum": upload_meta["chunk_nums"],
            "currentNum": chunk["order"],
            "chunkSize": chunk["size"],
            "chunk": image_to_base64_str(meta["dir"]+upload_meta["chunk_dir"]+chunk["name"]),
            "chunkValider": chunk["md5"],
            "videoValider": upload_meta["sha256"]
            }
            # print(chunk_format) 
            # exit(0)   
            res = send_json_request(
               url = url_video,
               retries = retry,
               payload = chunk_format
            ) 
            if res["success"]:
                if chunk["order"] in  res["data"]["data"]["okList"]:
                    chunk["pended"]=True
                    logger.info("video::{id} 分块{idx}上传完成".format(id=meta["id"],idx=chunk["name"]))
                else:
                    meta["video_ok"]=False
                    logger.info("video::{id} 分块{idx}上传失败".format(id=meta["id"],idx=chunk["name"])) 
            else:
                meta["video_ok"]=False
                logger.info("video::{id} 分块{idx}上传失败".format(id=meta["id"],idx=chunk["name"])) 
                break
    save_yaml(file,meta)                  


def drop_first_line(file_path):
    """删除文件第一行并覆盖（最快实现）"""
    with open(file_path, 'r+', encoding='utf-8') as f:
        lines = f.readlines()       # 一次性读取所有行
        f.seek(0)                   # 移动指针到文件开头
        f.writelines(lines[1:])     # 写入第二行之后的内容
        f.truncate()                # 截断剩余部分

def recycle(file,pending_lock,pending_file,recycle_lock,recycle_file):
    meta = load_yaml(file)
    if meta is None:
        with pending_lock:
            drop_first_line(pending_file)
        with recycle_lock:
            appendafile(recycle_file,file)
    elif meta["image_ok"] and meta["video_ok"]:
        with pending_lock:
            drop_first_line(pending_file)
        with recycle_lock:
            appendafile(recycle_file,file)




def work(gloabal_conf,self_conf):
    global ttk
    env = gloabal_conf["environment"]
    pending_lock = FileLock(env+gloabal_conf["share"]["pending"]["lock"])
    recycle_lock = FileLock(env+gloabal_conf["share"]["recycle"]["lock"])
    pending_file = env+ gloabal_conf["share"]["pending"]["file"]
    recycle_file = env+ gloabal_conf["share"]["recycle"]["file"]
    logger.info(" 开始检测文件")
    while True:
        file = None
        update_ttk()
        time.sleep(ttk)
        with pending_lock:
            file = readafile(pending_file)
        if not file:
            continue
        logger.info("开始上传文件::  "+file)
        init_meta(file)
        split_video(file,self_conf["pending"]["part_size"])
        upload_image(file,self_conf["pending"]["retry"],self_conf["pending"]["url"]["image"])
        upload_video(file,self_conf["pending"]["retry"],self_conf["pending"]["url"]["req"],self_conf["pending"]["url"]["video"])
        recycle(file,pending_lock,pending_file,recycle_lock,recycle_file)


def split_file(input_file, output_dir, chunk_size):
    """
    将文件拆分为多个部分，并返回分片信息列表    
    """
    
    os.makedirs(output_dir, exist_ok=True)
    part_num = 0
    file_info_list = []  # 存储分片信息的列表

    with open(input_file, "rb") as f:
        while True:
            chunk = f.read(chunk_size)
            if not chunk:
                break

            # 生成分片文件名（part001.bin）
            output_file = os.path.join(output_dir, f"part{part_num:03d}.bin")
            
            # 写入分片文件
            with open(output_file, "wb") as chunk_file:
                chunk_file.write(chunk)
            
            # 记录分片信息（文件名、大小、编号）

            file_info = {
                "name": os.path.basename(output_file),  # 文件名
                "size": len(chunk),         # 文件大小（B，保留2位小数）
                "order": part_num,                       # 分片编号
                "pended":False,
                "md5": hash.md5(output_file)
            }
            file_info_list.append(file_info)
            part_num += 1

    return file_info_list
